/*
 * Editor.java
 */

package xq.swing;

import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.Insets;
import java.awt.Point;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.UIManager;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.DocumentEvent;
import javax.swing.text.Document;
import javax.swing.text.EditorKit;
import xq.swing.borders.LineBorder;
import xq.swing.text.DocumentAdapter;
import static xq.util.CommonUtil.equal;

/**
 *
 * @author ThorntonRP
 */
public class Editor extends JPanel {

	private static final long serialVersionUID = 6502838358318582813L;

	/*-- Bound properties ------------------------------------------------*/

	private File file;
	private String contentType;
	private boolean modified;

	/*-- Components ------------------------------------------------------*/

	private TextPane textPane;
	private JPanel toolPanel;
	private JScrollPane textScrollPane;
	private LineNumberPane lineNumberPane;

	/*--------------------------------------------------------------------
	 * Constructors
	 *--------------------------------------------------------------------*/

	public Editor() {
		initComponents();
	}

	public Editor(String contentType) {
		this();
		textPane.setContentType(contentType);
	}

	public Editor(String contentType, File file) throws IOException {
		this();
		open(contentType, file);
	}

	/*--------------------------------------------------------------------
	 * Public Fields
	 *--------------------------------------------------------------------*/

	public File getFile() {
		return file;
	}

	public void setFile(File file) {
		Object oldValue = this.file;
		Object newValue = file;
		if (!equal(oldValue, newValue)) {
			this.file = file;
			firePropertyChange("file", oldValue, newValue);
		}
	}

	public Document getDocument() {
		return textPane.getDocument();
	}

	public void setDocument(Document document) {
		textPane.setDocument(document);
	}

	public String getText() {
		return textPane.getText();
	}

	public void setText(String text) {
		textPane.setText(text);
	}

	public boolean isModified() {
		return modified;
	}

	public void setModified(boolean modified) {
		boolean oldValue = this.modified;
		boolean newValue = modified;
		if (oldValue != newValue) {
			this.modified = modified;
			firePropertyChange("modified", oldValue, newValue);
		}
	}

	public String getContentType() {
		return textPane.getContentType();
	}

	public void setContentType(String contentType) {
		this.contentType = contentType;
		textPane.setContentType(contentType);
	}

	/*--------------------------------------------------------------------
	 * Public Operations
	 *--------------------------------------------------------------------*/

	public void setEditorKitForContentType(String contentType, EditorKit editorKit) {
		textPane.setEditorKitForContentType(contentType, editorKit);
	}

	public void open(String contentType, File file) throws IOException {
		this.contentType = contentType;
		this.file = file;
		readFile();
		textPane.setContentType(contentType);
	}

	public void save() throws IOException {
		if (file == null) {
			throw new IllegalStateException("File not set");
		} else {
			textPane.write(new FileWriter(file));
		}
	}

	public void save(File file) throws IOException {
		this.file = file;
		save();
	}

	public void revert() throws IOException {
		readFile();
	}

	@Override
	public boolean requestFocusInWindow() {
		boolean result = super.requestFocusInWindow();
		textPane.requestFocusInWindow();
		return result;
	}

	/*--------------------------------------------------------------------
	 * Protected Fields
	 *--------------------------------------------------------------------*/
	
	protected TextPane getTextPane() {
		return textPane;
	}
	
	protected JPanel getToolPanel() {
		return toolPanel;
	}
	
	/*--------------------------------------------------------------------
	 * Private Operations
	 *--------------------------------------------------------------------*/
	
	private void initComponents() {
		Font font = new Font("Monospaced", Font.PLAIN, 13);

		textPane = new TextPane();
		textPane.setFont(font);
		textPane.setMargin(new Insets(2, 2, 0, 0));
		textPane.getDocument().addDocumentListener(new DocumentModificationHandler());

		textScrollPane = new JScrollPane(textPane);
		textScrollPane.getViewport().setView(textPane);
		textScrollPane.setBorder(null);

		lineNumberPane = new LineNumberPane();
		lineNumberPane.bindTo(textPane);
		
		toolPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
		toolPanel.setBorder(new LineBorder(UIManager.getColor("controlShadow"), new Insets(0, 0, 1, 0), new Insets(0, 0, 0, 0)));
		toolPanel.setBackground(UIManager.getColor("TabbedPane.selected"));

		// Layout the controls
		this.setLayout(new BorderLayout());
		this.add(toolPanel, BorderLayout.NORTH);
		this.add(lineNumberPane, BorderLayout.WEST);
		this.add(textScrollPane, BorderLayout.CENTER);

		textScrollPane.getViewport().addChangeListener(new TextScrollChangeHandler());

	}

	private void readFile() throws IOException {
		if (file == null) {
			throw new IllegalStateException("File not set");
		} else {
			textPane.setContentType(contentType);
			textPane.read(new FileReader(file), null);
		}
	}

	/*--------------------------------------------------------------------
	 * Private Inner Classes
	 *--------------------------------------------------------------------*/

	private class DocumentModificationHandler extends DocumentAdapter {

		@Override
		public void update(DocumentEvent e) {
			setModified(modified);
		}
	}

	private class TextScrollChangeHandler implements ChangeListener {

		@Override
		public void stateChanged(ChangeEvent e) {
			Point position = textScrollPane.getViewport().getViewPosition();
			lineNumberPane.getViewport().setViewPosition(position);
		}
	}
}
